// ValidationAspects
// Written by Mike Saunders
// Released under Microsoft Public License (Ms-PL)
using System.Collections.Generic;
using System.Reflection;
using ValidationAspects.Core;
using ValidationAspects.Exceptions;

namespace ValidationAspects.Sdk.Interception
{
	public static class InterceptValidate
	{
		public static void Property(PropertyInfo property, object instance, object value)
		{
			Enforce.IsNotNull(property, "property");

			if (!ValidationSettings.InterceptValidateProperties)
				return;

			IValidator validator = property.ResolveValidator();
			if (validator == null)
				return;

			IValidationContext propertyContext = new ValidationContext(instance, property, value);

			try
			{
				validator.Invoke(value, propertyContext);
			}
			catch (ValidationException e)
			{
				e.SetContext(propertyContext);
				throw;
			}
		}

		public static void Method(MethodBase method, object instance, object[] values)
		{
			Enforce.IsNotNull(method, "method");

			List<ValidationException> exceptions = new List<ValidationException>();
			IValidationContext methodContext = new ValidationContext(instance, method.DeclaringType, null, method, null, values);

			// perform method pre-condition validation
			if (method.CanValidate())
			{
				IValidator validator = method.ResolveValidator();
				if (validator != null)
				{
					try
					{
						validator.Invoke(instance, methodContext);
					}
					catch (ValidationException e)
					{
						e.SetContext(methodContext);
						exceptions.Add(e);
					}
				}
			}

			// perform method parameter validation	
			if (values != null)
			{
				int index = 0;
				ParameterInfo[] parameters = method.GetParameters();

				while (index < values.Length)
				{
					ParameterInfo parameter = parameters[index];
					if (!parameter.IsOut)
					{
						IValidator validator = parameter.ResolveValidator();
						if (validator != null)
						{
							IValidationContext parameterContext = new ValidationContext(instance, method.DeclaringType, null, method, parameter, values);
							try
							{
								validator.Invoke(values[index], parameterContext);
							}
							catch (ValidationException e)
							{
								e.SetContext(parameterContext);
								exceptions.Add(e);
							}
						}
					}
					++index;
				}
			}

			if (exceptions.Count > 0)
			{
				ValidationException exception = new ValidationContextException(exceptions);
				exception.SetContext(methodContext);
				throw exception;
			}
		}
	}
}